#include "ngx_memorypool.h"

#include <cstdlib>

NgxMemoryPool::NgxMemoryPool(size_t size)
{
    ngx_pool_t  *p;

    pool_ = (ngx_pool_t*)malloc(size);
    if (pool_ == nullptr) {
        throw std::bad_alloc();
    }

    pool_->d.last = (u_char *) pool_ + sizeof(ngx_pool_t);
    pool_->d.end = (u_char *) pool_ + size;
    pool_->d.next = nullptr;
    pool_->d.failed = 0;

    size = size - sizeof(ngx_pool_t);
    pool_->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;

    pool_->current = pool_;
    pool_->large = nullptr;
    pool_->cleanup = nullptr;
}

NgxMemoryPool::~NgxMemoryPool()
{
    ngx_pool_t          *p, *n;
    ngx_pool_large_t    *l;
    ngx_pool_cleanup_t  *c;

    // 释放外部资源
    for (c = pool_->cleanup; c; c = c->next)
    {
        if (c->handler)
        {
            c->handler();
        }
    }

    // 释放大块内存
    for (l = pool_->large; l; l = l->next)
    {
        if (l->alloc)
        {
            free(l->alloc);
        }
    }

    // 释放小块内存
    for (p = pool_, n = pool_->d.next; /* void */; p = n, n = n->d.next)
    {
        free(p);

        if (n == nullptr)
        {
            break;
        }
    }
}

void NgxMemoryPool::ngx_reset_pool()
{
    ngx_pool_t        *p;
    ngx_pool_large_t  *l;
    ngx_pool_cleanup_t  *c;

    // 遍历cleanup外部资源释放链表，释放外部资源
    for (c = pool_->cleanup; c; c = c->next)
    {
        if (c->handler)
        {
            c->handler();
        }
    }

    // 遍历large链表（大块内存池链表），释放大块内存
    for (l = pool_->large; l; l = l->next)
    {
        if (l->alloc)
        {
            free(l->alloc);
        }
    }

    // 第一块内存池的头为ngx_pool_t，后面的内存池的头只有ngx_pool_data_t
    // 处理第一块内存池
    p = pool_;
	p->d.last = (u_char *) p + sizeof(ngx_pool_t);
	p->d.failed = 0;
	
	// 第二块内存池开始循环到最后一个内存池
	for (p = p->d.next; p; p = p->d.next)
    {
        p->d.last = (u_char *) p + sizeof(ngx_pool_data_t);
        p->d.failed = 0;
    }

    pool_->current = pool_;	    // 重置current指针
    pool_->large = nullptr;		// 重置指向大块内存池链表指针
    pool_->cleanup = nullptr;   // 重置cleanup指针
}

bool NgxMemoryPool::ngx_pfree(void *p)
{
    ngx_pool_large_t  *l;

    // 在large链表中找到当前要释放的大块内存
    // 若找到，释放该大块内存，并且将alloc置为nullptr
    for (l = pool_->large; l; l = l->next)
    {
        if (p == l->alloc)
        {
            free(l->alloc);
            l->alloc = nullptr;

            return true;
        }
    }
	// 若没有找到，说明该内存不是由内存池管理的，不进行释放
    return false;
}

bool NgxMemoryPool::ngx_pool_cleanup_add(ngx_pool_cleanup_pt callback)
{
    ngx_pool_cleanup_t  *c;

    c = (ngx_pool_cleanup_t*)ngx_palloc(sizeof(ngx_pool_cleanup_t));
    if (c == nullptr)
    {
        return false;
    }

    c->handler = callback;
    c->next = pool_->cleanup;

    pool_->cleanup = c;

    return true;
}

void* NgxMemoryPool::ngx_palloc(size_t size)
{
    if (size <= pool_->max)
    {
        return ngx_palloc_small(size, 1);
    }

    return ngx_palloc_large(size);
}

void* NgxMemoryPool::ngx_palloc_small(size_t size, bool align)
{
    u_char      *m;
    ngx_pool_t  *p;

    p = pool_->current;

    do
    {
        m = p->d.last;

        if (align)
        {
            m = ngx_align_ptr(m, NGX_ALIGNMENT);
        }

        if ((size_t) (p->d.end - m) >= size)
        {
            p->d.last = m + size;

            return m;
        }

        p = p->d.next;

    } while (p);

    return ngx_palloc_block(size);
}

void* NgxMemoryPool::ngx_palloc_block(size_t size)
{
    u_char      *m;
    size_t       psize;
    ngx_pool_t  *p, *new_ptr;

    psize = (size_t) (pool_->d.end - (u_char *) pool_);

    m = (u_char*)malloc(psize);
    if (m == nullptr)
    {
        return nullptr;
    }

    new_ptr = (ngx_pool_t *) m;

    new_ptr->d.end = m + psize;
    new_ptr->d.next = nullptr;
    new_ptr->d.failed = 0;

    m += sizeof(ngx_pool_data_t);
    m = ngx_align_ptr(m, NGX_ALIGNMENT);
    new_ptr->d.last = m + size;

    for (p = pool_->current; p->d.next; p = p->d.next)
    {
        if (p->d.failed++ > 4)
        {
            pool_->current = p->d.next;
        }
    }

    p->d.next = new_ptr;

    return m;
}

void* NgxMemoryPool::ngx_palloc_large(size_t size)
{
    void              *p;
    u_int              n;
    ngx_pool_large_t  *large;

    p = malloc(size);
    if (p == nullptr)
    {
        return nullptr;
    }

    n = 0;

    for (large = pool_->large; large; large = large->next)
    {
        if (large->alloc == nullptr)
        {
            large->alloc = p;
            return p;
        }

        if (n++ > 3) {
            break;
        }
    }

    large = (ngx_pool_large_t*)ngx_palloc_small(sizeof(ngx_pool_large_t), 1);
    if (large == nullptr)
    {
        free(p);
        return nullptr;
    }

    large->alloc = p;
    large->next = pool_->large;
    pool_->large = large;

    return p;
}